﻿using Microsoft.Extensions.DependencyInjection;
using mozhi.smarterp.Finance.Accounts;
using mozhi.smarterp.Finance.Basedatas.Events;
using mozhi.smarterp.Finance.Basedatas.Repository;
using mozhi.smarterp.Finance.Enums;
using mozhi.smarterp.Finance.Querys;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Guids;

namespace mozhi.smarterp.Finance.Basedatas
{
    public class BaseDataAppService<TEntity, TDto, TCreateDto, TUpdateDto> : FinanceAppService, IBaseDataAppService<TDto, TCreateDto, TUpdateDto>
        where TEntity : BaseDataAggregateRootBase
        where TDto : BaseDataDto
        where TCreateDto : BaseDataCreateDto
        where TUpdateDto : BaseDataUpdateDto
    {
        protected IBaseDataRepository<TEntity> _repository;
        protected IGuidGenerator _guidGenerator;
        protected IDistributedEventBus _distributedEventBus;
        protected string _moduleCodeType;

        public BaseDataAppService(IBaseDataRepository<TEntity> repository, IDataAssociationRepository dataAssociationRepository,
            IGuidGenerator guidGenerator, IDistributedEventBus distributedEventBus)
            : base(dataAssociationRepository)
        {
            _repository = repository;
            _guidGenerator = guidGenerator;
            _distributedEventBus = distributedEventBus;
        }

        public BaseDataAppService(IServiceProvider serviceProvider)
            : base(serviceProvider)
        {
            _repository = serviceProvider.GetService<IBaseDataRepository<TEntity>>();
            _guidGenerator = serviceProvider.GetService<IGuidGenerator>();
            _distributedEventBus = serviceProvider.GetService<IDistributedEventBus>(); ;
        }

        public BaseDataAppService(IServiceProvider serviceProvider , string moduleCode)
            : base(serviceProvider)
        {
            _repository = serviceProvider.GetService<IBaseDataRepository<TEntity>>();
            _guidGenerator = serviceProvider.GetService<IGuidGenerator>();
            _distributedEventBus = serviceProvider.GetService<IDistributedEventBus>();
            _moduleCodeType = moduleCode;
        }

        public virtual async Task<PagedResultDto<TDto>> FindAsync(PageQuery query)
        {
            var entities = await _repository.FindAsync(query);

            var count = await _repository.GetCountAsync(query);

            var result = new PagedResultDto<TDto>();

            var items = ObjectMapper.Map<List<TEntity>, List<TDto>>(entities);

            result.TotalCount = count;
            result.Items = items as IReadOnlyList<TDto>;

            return result;
        }

        public virtual async Task<TDto> GetAsync(Guid id)
        {
            var entity = await _repository.FindAsync(id);

            return ObjectMapper.Map<TEntity, TDto>(entity);
        }

        public virtual async Task<TDto> CreateAsync(TCreateDto dto)
        {
            await Validate(null, dto.Code, dto.Name);

            var entity = new BaseDataAggregateRootBase(_guidGenerator.Create(), dto.Code, dto.Name);

            entity = await _repository.InsertAsync(entity as TEntity);

            return ObjectMapper.Map<TEntity, TDto>(entity as TEntity);
        }

        public virtual async Task<TDto> UpdateAsync(TUpdateDto dto)
        {
            var entity = await _repository.GetAsync(dto.Id);

            if (entity == null)
                throw new UserFriendlyException("基础资料不存在", "100000");

            await Validate(dto.Id, dto.Code, dto.Name);

            entity.Change(dto.Code, dto.Name , dto.Activated);

            entity = await _repository.UpdateAsync(entity);

            return ObjectMapper.Map<TEntity, TDto>(entity);
        }

        public virtual async Task<TDto> DeleteAsync(Guid id)
        {
            var entity = await _repository.GetAsync(id);

            if (entity == null)
                throw new UserFriendlyException("基础资料不存在", "100000");

            await CheckDataAssociation(id);

            await _repository.DeleteAsync(entity);

            await _operationLogManager.LogAsync(_moduleCodeType, OperationType.Delete, entity, null);

            return ObjectMapper.Map<TEntity, TDto>(entity);
        }

        #region
        protected virtual async Task Validate(Guid? id, string code, string name)
        {
            var codeExistCount = await _repository.GetCountAsync(new PageQuery() { Keyword = code, PageIndex = 1, PageSize = int.MaxValue, ExclusionId = id });

            if (codeExistCount > 0)
                throw new UserFriendlyException("编码已存在", "100000");

            var nameExistCount = await _repository.GetCountAsync(new PageQuery() { Keyword = name, PageIndex = 1, PageSize = int.MaxValue, ExclusionId = id });

            if (nameExistCount > 0)
                throw new UserFriendlyException("名称已存在", "100000");
        }

        protected virtual void ValidateAccount(Account matchAccount, string displayName)
        {
            if (matchAccount == null)
                throw new UserFriendlyException($"{displayName}不存在", "100000");

            if (!matchAccount.Activated)
                throw new UserFriendlyException($"{displayName}已禁用", "100000");

            if (!matchAccount.IsLeaf)
                throw new UserFriendlyException($"{displayName}不是最子级科目", "100000");
        }

        #endregion
    }
}
